﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.LiveFX.Client;
using System.Net;
using Microsoft.LiveFX.ResourceModel;
using System.Security;

namespace WindowsLiveID
{
    /// <summary>
    /// Type that helps interact with the Live Operating Environment.
    /// </summary>F
    public class WindowsLive
    {
        private string _url = "https://user-ctp.windows.net/";
        private LiveOperatingEnvironment _environment;
        private static WindowsLive _instance;

        /// <summary>
        /// Gets the Windows Live Authentication Token.
        /// </summary>
        public string Token { get; private set; }

        /// <summary>
        /// Gets an instance of the Live Operating Environment. This property will lazy-load and connect if required.
        /// </summary>
        public LiveOperatingEnvironment Environment
        {
            get {
                if (_environment == null)
                {
                    Connect();
                }

                return _environment;
            }
        }

        /// <summary>
        /// Gets an indication if the user has been authenticated.
        /// </summary>
        public bool IsAuthenticated { get; private set; }

        /// <summary>
        /// Static instance of the Windows Live helper type.
        /// </summary>
        public static WindowsLive Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new WindowsLive();
                }

                return _instance;
            }
        }

                /// <summary>
        /// Method that authenticate with persisted credentials.
        /// </summary>
        /// <returns>Windows Live Authentication Token.</returns>
        public string Authenticate()
        {
            if (string.IsNullOrEmpty(PersistedAddress) || string.IsNullOrEmpty(PersistedPassword))
            {
                throw new ApplicationException("Credentials are not persisted.");
            }

            // Decrypt the persisted password.
            string password = Bitter.Protect.UnprotectText(PersistedPassword, PersistedAddress);

            return Authenticate(PersistedAddress, password);
        }

        /// <summary>
        /// Method that authenticate an Windows Live ID.
        /// </summary>
        /// <param name="userId">Address of the Windows Live ID.</param>
        /// <param name="password">Password for the account.</param>
        /// <param name="persistUserId">Indicates if identity should be persisted.</param>
        /// <param name="persistPassword">Password persists only if identity is persisted.</param>
        /// <returns>Windows Live Authentication Token.</returns>
        public string Authenticate(string userId, string password, bool persistUserId, bool persistPassword)
        {
            // It was considered to use System.Security.SecureString as oppose to a regular string
            // for the in-memory password for added security. Since the interaction with LOE requires
            // creation of the NetworkCredential object, the password is "leaked" freely in the CLR-memory
            // and therefor the use of SecureString is less useful.
            // Ref: http://msdn.microsoft.com/en-us/library/system.security.securestring.aspx

            if (persistUserId)
            {
                PersistedAddress = userId;

                if (persistPassword)
                {
                    // We will only bother with encrypting the users password, not the address.
                    PersistedPassword = Bitter.Protect.ProtectText(password, userId);
                }
                else
                {
                    PersistedPassword = string.Empty;
                }
            }
            else
            {
                PersistedAddress = string.Empty;
                PersistedPassword = string.Empty;
            }

            string token = Authenticate(userId, password.ToString());

            // We only save the settings if the authentication works.
            Properties.Settings.Default.Save();

            return token;
        }

        /// <summary>
        /// Method that authenticate an Windows Live ID.
        /// </summary>
        /// <param name="userId">Address of the Windows Live ID.</param>
        /// <param name="password">Password for the account.</param>
        /// <returns></returns>
        public string Authenticate(string userId, string password)
        {
            string token = new NetworkCredential(userId, password, _url).GetWindowsLiveAuthenticationToken();

            Token = token;
            IsAuthenticated = true;

            return token;
        }

        /// <summary>
        /// Connects to the Live Operating Environment with the authentication token provided during authentication.
        /// </summary>
        /// <returns>Instance of the Live Operating Environment.</returns>
        public LiveOperatingEnvironment Connect()
        {
            return Connect(Token);
        }

        /// <summary>
        /// Connects to the Live Operating Environment with the authentication token provided during authentication.
        /// </summary>
        /// <param name="token">Different than the instance token.</param>
        /// <returns>Instance of the Live Operating Environment.</returns>
        public LiveOperatingEnvironment Connect(string token)
        {
            if (!IsAuthenticated)
            {
                throw new ApplicationException("You have to authenticate before calling Connect.");
            }

            Uri uri = new Uri(_url);

            LiveItemAccessOptions accessOptions = new LiveItemAccessOptions(true);

            LiveOperatingEnvironment env = new LiveOperatingEnvironment();
            env.Connect(token, AuthenticationTokenType.UserToken, uri, accessOptions);
            
            _environment = env;

            return env;
        }

        /// <summary>
        /// Gets the Windows Live ID account address.
        /// </summary>
        public string PersistedAddress
        {
            get { return Properties.Settings.Default.WindowsLiveAddress; }
            private set { Properties.Settings.Default.WindowsLiveAddress = value; }
        }

        /// <summary>
        /// Gets the Windows Live ID account password in encrypted form.
        /// </summary>
        public string PersistedPassword
        {
            get { return Properties.Settings.Default.WindowsLivePassword; }
            private set { Properties.Settings.Default.WindowsLivePassword = value; }
        }

        /// <summary>
        /// Gets and sets the property for signing in automatically.
        /// </summary>
        public bool SignInAutomatically
        {
            get { return Properties.Settings.Default.WindowsLiveSignInAutomatically; }
            set { Properties.Settings.Default.WindowsLiveSignInAutomatically = value; Properties.Settings.Default.Save(); }
        }

        /// <summary>
        /// Method that tires to automatically login. Returns a boolean value if it's able to login using automatic logon.
        /// </summary>
        /// <returns></returns>
        public bool TryAutomaticLogon()
        {
            if (string.IsNullOrEmpty(PersistedAddress) || string.IsNullOrEmpty(PersistedPassword))
            {
                return false;
            }

            try
            {
                Authenticate();
            }
            catch
            {
            
            }

            return IsAuthenticated;
        }
    
        /// <summary>
        /// Signs out the user and removes the instance of Live Operating Environment.
        /// </summary>
        public void SignOut()
        {
            _environment = null;
            Token = string.Empty;
            IsAuthenticated = false;
        }
    }
}
